
#
# (C) Copyright 2010- Hewlett-Packard Development Company, L.P.
# 
#  $Id:$
#

package Script_Base;

=head1 NAME

Script_Base - a module that act as the base of all instance config scripts

=head1 DESCRIPTION

A Perl class that act as the base class of all instance config script.
It provide some basic functions, to ensure that all scripts can use the same 
way for log, check status ...

=head1 CLASS DATA

=head2 CONSTANTS

=over 4
=item * C<OK> = 0
=item * C<WARN> = 1
=item * C<CRITICAL> = 2
=item * C<UNKNOWN> = 3

=item * C<PROCEED> = 0
=item * C<DONE> = 1

=back

=head2 FUNCTIONS

=over 4

=item C<new()>

Returns a new object of Script_base class. This function requires the name of
the script, and optional, the userid of the squser.

=item C<log_msg()>
This function is called to display an info in instance_log.
The function requires the option for the message to display.

=item C<log_warn()>
This function is called to display a warning message in instance_log
The function requires the option for the message to display.

=item C<logerr_n_die()>
This function is called to display an error message in both STDERR and nstance_log,
and it will cause the program exit with code CRITICAL.
The function requires the option for the message to display.

=item C<run_cmd()>
This function is called to execute a shell command.
It requires the option string for the shell command.

=item C<run_cmd2()>
This function is called to execute a shell command, and return the standard output
as one string.
It requires the option string for the shell command.

=item C<get_status()>
This function is called to get the running status of current script.
It requires no option.

=item C<set_status()>
This function is called to set the running status of current script.
It requires the option string for the status.

=item C<set_args()>
This function is called to set all the arguments.
It requires the option as a ref to a hash to represent the argument name 
and argument value.

=item C<pre_check()>
This function is called to check for any requirements that must be met before 
the configuration is performed.
This function is empty in the Script_Base class, it should be overwritten by any 
derived class if any check is required.

=item C<post_check()>
This function verifies that the work done is complete and in such a state 
 that it is ready for the next step to be done
This function is empty in the Script_Base class, it should be overwritten by any 
derived class if any check is required.

=item C<run()>
This function is the main function of the script.
It will call the pre_check, perform_config and post_check. It is not recommended to 
overwrite this function in any derived class.

=back

=head1 COPYRIGHT

 (C) Copyright 2010- Hewlett-Packard Development Company, L.P.

=cut

require Exporter;

@ISA = qw(Exporter);

@EXPORT = qw(OK 
             WARN 
             CRITICAL 
             UNKNOWN
             DONE
             PROCEED
             log_msg 
             log_warn 
             logerr_n_die
             run_cmd
             run_cmd2);

use strict;
use 5.008000;
use warnings;
use Params::Validate qw(:all);

use POSIX;
use File::Basename;

use Data::Dumper;

use config ':all';

# possible error returns
use constant {
    OK => 0,
    WARN => 1,
    CRITICAL => 2,
    UNKNOWN => 3,
};

use constant {
    DONE => 0,
    PROCEED => 1
};

sub new($;@)
{
    my $proto = shift;

    my $class = ref($proto) || $proto;

    my %args = validate(@_, { SCRIPT => {type => SCALAR},
                              STATUS_FILE => 0,
                            }
                       );

    my $self = {};

    $self->{SCRIPT} = $args{SCRIPT};

    my $status_path = "$nvpath/instance/status/";
    if ($args{STATUS_FILE}) {
        $self->{STATUS_FILE} = $status_path . $args{STATUS_FILE} . ".stat";
    }
    else {
        $self->{STATUS_FILE} = $status_path . $args{SCRIPT} . ".stat";
    }

    bless ($self, $class);

    return $self;

}

#functions to export, not attached to any object
sub log_msg($)
{    
    my $message = shift;
    my $infomessage;

    $infomessage = basename($0) . ": INFO: ". $message . "\n";
    instance_log_event($infomessage);

}

sub log_warn($)
{
    my $message = shift;
    my $warnmessage;

    $warnmessage = basename($0) . ": WARNING: ". $message . "\n";
    instance_log_event($warnmessage);

}

sub logerr_n_die($)
{
    my $message = shift;
    my $errormessage;

    $errormessage = basename($0) . ": ERROR: ". $message . "\n";
    print STDERR $errormessage;
    
    instance_log_event($errormessage);
    exit(CRITICAL);

}

sub run_cmd($)
{
    my $cmd = shift;

    system($cmd);

    if (WIFEXITED($?) && (WEXITSTATUS($?) != 0)) {
        logerr_n_die("An error occured when execute the cmd $cmd:" . WEXITSTATUS($?));
    }
}

sub run_cmd2($)
{
    my $cmd = shift;

    chomp (my $output = `$cmd`);

    if (WIFEXITED($?) && (WEXITSTATUS($?) != 0)) {
        logerr_n_die("An error occured when execute the cmd $cmd:" . WEXITSTATUS($?));
    }

    return($output);
}

# functions don't export, must attached to an object
sub get_status($)
{
    my $self = shift;
    die "invalid reference for \$self\n" unless (ref $self);

    my $prefix = $self->{squser} ? $self->{squser} . ':' : '';

    return (PROCEED) if (! -e $self->{STATUS_FILE});

    open (IFH, $self->{STATUS_FILE}) ||
        logerr_n_die("Can't open status file $self->{STATUS_FILE}: $!");

    while (<IFH>) {
        next if (/^\s*$/);
        next if (/^\#/);

        if (/^${prefix}completed/) {
            close (IFH);
            return (DONE);
        }
    }
    close (IFH);
    return (PROCEED);
}

sub set_status($$)
{
    my $self = shift;
    my $statusmsg = shift;

    die "invalid reference for \$self\n" unless (ref $self);

    open (OFH, ">>$self->{STATUS_FILE}") ||
        logerr_n_die("Can't open status file $self->{STATUS_FILE}: $!");

    my $prefix = $self->{squser} ? $self->{squser} . ':' : '';
    print OFH $prefix . $statusmsg . "\n";

    close (OFH);
}

sub set_args($$)
{
    my $self = shift;
    my $args = shift;

    die "invalid reference for \$self\n" unless (ref $self);

    while (my ($key, $val) = each (%{$args})) {
        $self->{$key} = $val;
    }
}

sub pre_check($)
{
    my $self = shift;

    return (OK);
}

sub post_check($)
{
    my $self = shift;

    return (OK);
}

sub run($)
{
    my $self = shift;
    die "invalid reference for \$self\n" unless (ref $self);

    log_msg("Starting script: revision number $self->{rev}") if ($self->{rev});

    $self->pre_check;

    $self->perform_config;

    $self->post_check;

    exit(OK);
}

1;
